var api = acre.require("lib/promise/apis.sjs");
var deferred = api.deferred;
var freebase = api.freebase;

var url = "http://schema.org/";
var domain = "/base/svocab";

var types = function(category) {
  var q = acre.require("category-query2.mql");
  
  if (category) {
    q = q.extend({
      "!/base/svocab/category/member.name": category,
      "/freebase/type_hints/included_types.!/base/svocab/category/member.name": category,
      "!/freebase/type_hints/included_types.!/base/svocab/category/member.name": category
    });
  }
  return freebase.mqlread(q.query)
    .then(function(env) {
      return env.result;
    })
    .then(function(result) {
      var types = {};
      var promises = [];
      result.forEach(function(res) {
        var type = {
          "name" : res.name,
          "supertypes_dict": {},
          "subtypes_dict": {},
          "subtypes": []
        };
        
        res["/freebase/type_hints/included_types"].forEach(function(t) {
          type.has_supertypes = true;
          type.supertypes_dict[t.name] = true;
        });
        
        res["!/freebase/type_hints/included_types"].forEach(function(t) {
          type.has_subtypes = true;
          type.subtypes_dict[t.name] = true;
        });
        
        if (res["/common/topic/article"]) {
          promises.push(freebase.get_blob(res["/common/topic/article"].id, "blurb")
            .then(function(result) {
              type.description = result.body;
            }));
        }
      
        types[type.name] = type;

      });
      
      return deferred.all(promises)
        .then(function() {
          return types;
        })
    })
    .then(function(types) {
      var category = [];
      for (var t in types) {
        var type = types[t];
        for (var st in type.subtypes_dict) {
          var subtype = types[st];
          var has_intermediate_type = false;
          for (var spt in subtype.supertypes_dict) {
            var supertype = types[spt];
            if (spt !== t && spt in type.subtypes_dict) {
              has_intermediate_type = true;
              break;
            }
          }
          if (!has_intermediate_type) {
            type.subtypes.push(subtype);
          }
        }
        if (!type.has_supertypes) {
          category.push(type);
        }
      }
      return category;
    });
};

var type = function(typename) {
  var q = acre.require("types-query.mql")
    .extend({"name":typename})
    .query[0];

  return freebase.mqlread(q)
    .then(function(env) {
      return env.result;
    })
    .then(function(t) {
      var type = format_type(t);
      var promises = [];

      // fill in description
      if (t["/common/topic/article"]) {
        promises.push(freebase.get_blob(t["/common/topic/article"].id, "blurb")
          .then(function(result) {
            type.description = result.body;
          }));
      }

      // fill in enumerated values
      var enumq = acre.require("enumeration-query");
      function ect_values(ect) {
        promises.push(freebase.mqlread(enumq.extend({name:ect.name}).query)
          .then(function(env) {
            var e = env.result[0];
            ect.values = e.instance;
            ect.enumeration_kind = e["/base/svocab/meta/enumeration_kind"].name;
          }));
      }
    
      type.properties.forEach(function(prop) {
        var ect = prop.expected_type[0];
        if (ect.enumeration) ect_values(ect);
      });
    
      type.supertypes.forEach(function(t) {
        t.properties.forEach(function(prop) {
          var ect = prop.expected_type[0];
          if (ect.enumeration) ect_values(ect);
        });
      });


      return deferred.all(promises)
        .then(function() {
          return type;
        });
      })
};

var categories = function(name) {
  var name = name || null;
  var q = acre.require("categories-query").extend({name:name}).query;

  return freebase.mqlread(q)
  .then(function(env) {
    return env.result;
  })
  .then(function(categories) {
    return categories.map(function(c) {
      var types = c.member.map(function(t) {
        return {
          "name" : t.name,
          "itemtype" : url + t.name,
        }
      });
      return {
        "name": c.name,
        "types": types
      }
    });
  });
};

function format_type(res, exclude_enumerations) {
  var type = {
    name: res.name,
    itemtype: url + res.name,
    description: "",
    category: (res["!/base/svocab/category/member"] ? res["!/base/svocab/category/member"].name : null),
    domain: res.domain,
    enumeration: !!res["/freebase/type_hints/enumeration"],
    properties: res.properties.map(format_prop),
  };


  type.properties = type.properties.filter(function(prop) {
    if (exclude_enumerations && prop.expected_type[0].enumeration) {
      return false;
    } else if (prop.hidden) {
      return false;
    } else {
      return true;
    }
  });

  if (res["!/freebase/type_hints/included_types"]) {
    type.subtypes = res["!/freebase/type_hints/included_types"].map(format_ect);
  }

  if (res["/freebase/type_hints/included_types"]) {
    type.supertypes = res["/freebase/type_hints/included_types"].map(format_type, true);
  }

  return type;
}

function format_prop(prop) {
  var property = {
    name: prop.name,
    itemprop: url /* + t.name */ + "/" + prop.name,
    description: prop["/freebase/documented_object/tip"] || "",
    hidden: prop["/freebase/property_hints/display_none"],
    expected_type: [format_ect(prop.expected_type)]
  };
  property.expected_type.concat(prop["/base/svocab/meta/expected_type"].map(format_ect));
  return property;
}

function format_ect(ect) {
  return {
    name: ect.name,
    itemtype: ect.domain == domain ? url + ect.name : null,
    enumeration: ect["/freebase/type_hints/enumeration"] ? true : false
  };
};